package reci.journal;

import java.awt.GridLayout;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Properties;

import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import reci.journal.utils.Crypter;
import reci.journal.utils.DateTimeMacro;
import reci.journal.utils.ExtUtil;
import reci.journal.utils.Zipper;

/*
 * Copyright (C) 2014 ReciGames
 * See notice in reci.journal.Main.java.
 */

/**journal settings, functions*/
public class Journal
{
	//default settings
	private static final String SETTINGS_FILENAME=".settings";
	private static final String DEFAULT_NEW_ENTRY_PATH="entries/%yyyy%-%MM%-%dd%/entry.rtf";
	private static final String DEFAULT_NEW_ENTRY_COMMAND="";
	private static final String DEFAULT_TEMPLATE_PATH="default-template.rtf";
	
	/**path to .rj file*/
	public static String rjPath;
	/**path of journal root directory*/
	public static String journalRoot;
	/**journal's password*/
	private static char[] password=new char[0];
	
	//user-defined settings
	public static String newEntryPathMacro;
	public static String newEntryCommandMacro;
	public static String templatePathMacro;
	public static boolean knowsToSave;
	
	/**
	 * @param rjPath
	 * @return if _.rj is password-protected
	 */
	public static boolean isPasswordProtected(String rjPath)
	{
		return Zipper.canUnzip(rjPath);
	}
	
	/**
	 * Logs in to journal.
	 * 
	 * @param r
	 * @param p
	 * @throws Exception
	 */
	public static void login(String r, char[] p) throws Exception
	{
		rjPath=r;
		journalRoot=rjPath.substring(0,rjPath.lastIndexOf('.'))+File.separator;
		setPassword(p);
		
		unpackJournal(rjPath);
		
		loadSettings();
		
		GlobalSettings.setProperty(Keys.DEFAULT_RJPATH,rjPath);
	}
	
	/**
	 * Unpacks .rj file.
	 * 
	 * @param rjPath
	 * @throws Exception
	 */
	private static void unpackJournal(String rjPath) throws Exception
	{
		//_.rj doesn't exist (i.e. first login)
		if(!new File(rjPath).exists())
		{
			new File(journalRoot).mkdirs();
		}
		//_.rj exists
		else
		{
			String zipPath=rjPath;
	
			//decrypt to _.rj.zip
			if(hasPassword())
			{
				zipPath=Crypter.decrypt(rjPath,password);
			}
			
			//unzip _.zip to _/
			Zipper.unzip(zipPath,journalRoot);
			
			//delete _.rj.zip remnant
			if(hasPassword())
			{
				new File(zipPath).delete();
			}
		}
	}
	
	/**
	 * Loads settings.
	 * 
	 * @throws IOException
	 */
	private static void loadSettings() throws IOException
	{
		String settingsPath=getSettingsPath();
		boolean loadedProperties=false;
		
		//try to load from settings file
		try
		{
			Properties props=new Properties();
			BufferedReader br=new BufferedReader(new FileReader(settingsPath));
			props.load(br);
			br.close();
			
			newEntryPathMacro=props.getProperty(Keys.NEW_ENTRY_PATH,DEFAULT_NEW_ENTRY_PATH);
			newEntryCommandMacro=props.getProperty(Keys.NEW_ENTRY_COMMAND,DEFAULT_NEW_ENTRY_COMMAND);
			templatePathMacro=props.getProperty(Keys.TEMPLATE_PATH,DEFAULT_TEMPLATE_PATH);
			knowsToSave=props.getProperty(Keys.KNOWS_TO_SAVE,"").equals("1");
			
			loadedProperties=true;
		}
		catch(Exception e)
		{}
		
		//unable to load .settings
		if(!loadedProperties)
		{
			//set properties as defaults
			newEntryPathMacro=DEFAULT_NEW_ENTRY_PATH;
			newEntryCommandMacro=DEFAULT_NEW_ENTRY_COMMAND;
			templatePathMacro=DEFAULT_TEMPLATE_PATH;
			knowsToSave=false;
			
			//copy default template
			InputStream is=Journal.class.getResourceAsStream("/"+DEFAULT_TEMPLATE_PATH);
			ExtUtil.copyAndClose(is,journalRoot+DEFAULT_TEMPLATE_PATH);
		}
	}
	
	/**
	 * Logs out of journal.
	 * 
	 * @return if ready to logout
	 * @throws Exception
	 */
	public static boolean logout() throws Exception
	{
		if(!knowsToSave && !remindToSave())
		{
			return false;
		}
		
		saveSettings();
		packJournal();
		clearPassword();
		
		return true;
	}
	
	/**
	 * @return if ready to logout
	 */
	private static boolean remindToSave()
	{
		JPanel pane=new JPanel();
		pane.setLayout(new GridLayout(2,1));
		pane.add(new JLabel("<html>Before exiting "+Const.APP_NAME+", you must save and close all entries.<br>Are you sure you want to exit?</html>"));
		JCheckBox checkbox=new JCheckBox("<html><i>Don't show this message again.</i></html>");
		pane.add(checkbox);
		
		try
		{
			if(JOptionPane.showConfirmDialog(null,pane,"",JOptionPane.YES_NO_OPTION)!=JOptionPane.YES_OPTION)
			{
				//doesn't want to exit
				return false;
			}
			
			if(checkbox.isSelected())
			{
				knowsToSave=true;
			}
			
			return true;
		}
		catch(Exception e)
		{}
		
		return false;
	}
	
	/**
	 * Saves journal settings, global settings.
	 */
	private static void saveSettings()
	{
		//journal settings
		BufferedWriter out=null;
		
		try
		{
			String settingsPath=getSettingsPath();
			out=new BufferedWriter(new FileWriter(settingsPath));
			
			Properties props=new Properties();
			
			props.setProperty(Keys.NEW_ENTRY_PATH,newEntryPathMacro);
			props.setProperty(Keys.NEW_ENTRY_COMMAND,newEntryCommandMacro);
			props.setProperty(Keys.TEMPLATE_PATH,templatePathMacro);
			props.setProperty(Keys.KNOWS_TO_SAVE,knowsToSave?"1":"0");
			
			props.store(out,"Journal Settings");
			out.close();
		}
		catch(Exception e)
		{
			JOptionPane.showMessageDialog(null,"Error saving journal.");
		}
		finally
		{
			ExtUtil.close(out);
		}
		
		//global settings
		GlobalSettings.writeSettings();
	}
	
	/**
	 * Packs .rj file.
	 * 
	 * @throws Exception
	 */
	private static void packJournal() throws Exception
	{
		//zip journal
		Zipper.zip(journalRoot,rjPath);
		
		//encrypt journal
		if(hasPassword())
		{
			try
			{
				Crypter.encrypt(rjPath,password);
			}
			catch(Exception e)
			{
				ExtUtil.openFile(new File(Journal.journalRoot).getParent());
				throw e;
			}
		}
		
		//delete journalRoot
		if(!ExtUtil.deleteFile(new File(journalRoot)))
		{
			ExtUtil.openFile(Journal.journalRoot);
			throw new Exception("Unable to delete all of the unencrypted journal.");
		}
	}
	
	public static void setPassword(char[] p)
	{
		if(hasPassword())
		{
			Arrays.fill(password,(char)0);
		}
		
		password=p;
	}
	
	/**
	 * Clears password.
	 */
	public static void clearPassword()
	{
		setPassword(new char[0]);
	}
	
	/**
	 * @return if password-protected
	 */
	public static boolean hasPassword()
	{
		return password.length>0;
	}
	
	public static String getPassword()
	{
		return new String(password);
	}
	
//set settings
	public static void setNewFileNameMacro(String s)
	{
		newEntryPathMacro=s;
	}
	
	public static void setNewEntryCommandMacro(String s)
	{
		newEntryCommandMacro=s;
	}
	
	public static void setTemplatePathMacro(String s)
	{
		templatePathMacro=s;
	}
	
	private static String getSettingsPath()
	{
		return journalRoot+File.separator+SETTINGS_FILENAME;
	}
	
	/**
	 * Creates entry from template with Macro, then opens entry.
	 */
	public static void createEntry()
	{
		DateTimeMacro.init();
		
		String filepath=journalRoot+File.separator+DateTimeMacro.process(newEntryPathMacro);
		
		//create if file doesn't exist
		if(!new File(filepath).exists())
		{
			new File(filepath).getParentFile().mkdirs();
			String templatePath=journalRoot+File.separator+DateTimeMacro.process(templatePathMacro);
			
			boolean success=false;
			
			//write entry from template
			if(new File(templatePath).exists())
			{
				BufferedWriter bw=null;
				BufferedReader br=null;
				
				try
				{
					bw=new BufferedWriter(new FileWriter(filepath));
					br=new BufferedReader(new FileReader(templatePath));
					String line;
					
					//parse template and output resulting entry
					while((line=br.readLine())!=null)
					{
						bw.write(DateTimeMacro.process(line));
						bw.write("\r\n");
					}
					
					success=true;
				}
				catch(Exception e)
				{}
				finally
				{
					ExtUtil.close(bw);
					ExtUtil.close(br);
				}
			}
			
			//failed to write from template
			if(!success)
			{
				BufferedWriter bw=null;
				
				try
				{
					//make empty file
					bw=new BufferedWriter(new FileWriter(filepath));
				}
				catch(Exception e)
				{}
				finally
				{
					ExtUtil.close(bw);
				}
			}
		}
		
		//default open
		if(newEntryCommandMacro.equals(""))
		{
			ExtUtil.openFile(filepath);
		}
		//custom open
		else
		{
			ExtUtil.system(DateTimeMacro.process(newEntryCommandMacro)+" "+filepath);
		}
	}
}
